package com.nutiteq.mapper;

import java.io.IOException;
import java.util.Random;
import java.util.Timer;
import java.util.TimerTask;

import javax.microedition.lcdui.Alert;
import javax.microedition.lcdui.AlertType;
import javax.microedition.lcdui.Canvas;
import javax.microedition.lcdui.Command;
import javax.microedition.lcdui.CommandListener;
import javax.microedition.lcdui.Display;
import javax.microedition.lcdui.Displayable;
import javax.microedition.lcdui.Graphics;
import javax.microedition.lcdui.Image;
import javax.microedition.lcdui.List;
import javax.microedition.lcdui.game.Sprite;
import javax.microedition.midlet.MIDletStateChangeException;

import com.nutiteq.MapComponent;
import com.nutiteq.components.KmlPlace;
import com.nutiteq.components.OnMapElement;
import com.nutiteq.components.Place;
import com.nutiteq.components.PlaceLabel;
import com.nutiteq.components.WgsPoint;
import com.nutiteq.components.ZoomRange;
import com.nutiteq.controls.ControlKeys;
import com.nutiteq.kml.KmlUrlReader;
import com.nutiteq.listeners.ErrorListener;
import com.nutiteq.listeners.MapListener;
import com.nutiteq.listeners.OnMapElementListener;
import com.nutiteq.log.Log;
import com.nutiteq.mapper.forms.BoundingBoxForm;
import com.nutiteq.mapper.forms.ChangeCursorList;
import com.nutiteq.mapper.forms.ChangeMapList;
import com.nutiteq.mapper.forms.ChangeZoomIndicator;
import com.nutiteq.mapper.forms.GoToForm;
import com.nutiteq.mapper.forms.NetworkInformation;
import com.nutiteq.mapper.forms.PanningList;
import com.nutiteq.mapper.forms.RemovePlacesList;
import com.nutiteq.mapper.forms.ResizeForm;
import com.nutiteq.mapper.forms.SearchForm;
import com.nutiteq.mapper.forms.SetLocationForm;
import com.nutiteq.mapper.forms.SetZoomLevel;
import com.nutiteq.utils.DummyStarter;
import com.nutiteq.utils.InitializedCanvas;
import com.nutiteq.utils.LogForm;

public class MapScreen extends Canvas implements CommandListener, MapListener,
    OnMapElementListener, ErrorListener, InitializedCanvas {
  public static MapScreen instance;
  private final MapComponent mapDisplay;

  private final Command exit = new Command("Exit", Command.EXIT, 0);
  private final Command searchResultsCmd = new Command("Search results", Command.ITEM, 0);

  private Command resultsBack;
  private Command resultsCenter;
  private Command resultsClear;

  public static final WgsPoint TALLINN = new WgsPoint(24.764580, 59.437420);
  public static final WgsPoint TARTU = new WgsPoint(26.718200, 58.375230);

  private Image[] icons;
  private static final int ICON_SIZE = 14;

  private static final String[] PLACE_NAMES = new String[] { "Kivi hostel", "Kiuma house",
      "Guesthouse Vana Tall", "Innu farm", "Abruka house",
      "Vahtra house with verry long name that should be wrapped and turned and...",
      "Flora guesthouse", "Aegna  hostel", "Aegna camping", "Hostel" };
  private static final double[][] PLACE_COORDINATES = new double[][] {
      { 24.913810719954, 59.342171027004 }, { 26.95589798528, 58.058726688269 },
      { 25.451296861604, 58.888777439038 }, { 22.50406267826, 58.164964638039 },
      { 22.500539875936, 58.159058644506 }, { 22.511357754825, 58.156357100604 },
      { 24.745941190393, 59.57727265626 }, { 24.762913231878, 59.575766544475 },
      { 24.753353029386, 59.581943183553 }, { 25.649282945646, 59.266065790641 } };

  private Place[] places;

  private boolean initialized;

  private static final Random R = new Random();

  private Image splash;

  private final Timer timer;
  private Thread starter;
  private KmlPlace[] kmlPlaces = new KmlPlace[0];
  private Place[] searchPlaces;
  private List resultsList;

  public MapScreen() {
    instance = this;
  
    try {
      final Image tmp = Image.createImage("/icons.png");
      icons = new Image[tmp.getWidth() / ICON_SIZE];
      for (int x = 0; x < tmp.getWidth(); x += ICON_SIZE) {
        icons[x / ICON_SIZE] = Image
            .createImage(tmp, x, 0, ICON_SIZE, ICON_SIZE, Sprite.TRANS_NONE);
      }
    } catch (final IOException e) {
    }

    try {
      splash = Image.createImage("/splash.png");
    } catch (final IOException e) {
      e.printStackTrace();
    }

    timer = new Timer();
    timer.schedule(new TimerTask() {
      public void run() {
        starter = new Thread(new DummyStarter(Mapper.instance, instance));
        starter.start();
      }
    }, 3000);
    mapDisplay = new MapComponent("abcdtrial", Mapper.instance, getWidth(), getHeight(), TALLINN,
        10);
  }

  public void initialize() {
    mapDisplay.enableDownloadCounter();
    mapDisplay.enableDownloadDisplay();
    mapDisplay.startMapping();

    mapDisplay.setMapListener(this);
    mapDisplay.setOnMapElementListener(this);
    mapDisplay.setErrorListener(this);

    mapDisplay.defineControlKey(ControlKeys.MOVE_UP_KEY, -1);
    mapDisplay.defineControlKey(ControlKeys.MOVE_DOWN_KEY, -2);
    mapDisplay.defineControlKey(ControlKeys.MOVE_LEFT_KEY, -3);
    mapDisplay.defineControlKey(ControlKeys.MOVE_RIGHT_KEY, -4);
    mapDisplay.defineControlKey(ControlKeys.SELECT_KEY, -5);
    mapDisplay.defineControlKey(ControlKeys.ZOOM_OUT_KEY, Canvas.KEY_NUM1);
    mapDisplay.defineControlKey(ControlKeys.ZOOM_IN_KEY, Canvas.KEY_NUM3);
    mapDisplay.defineControlKey(ControlKeys.ZOOM_OUT_KEY, Canvas.KEY_STAR);
    mapDisplay.defineControlKey(ControlKeys.ZOOM_IN_KEY, Canvas.KEY_POUND);
    //MPowerPlayer
    mapDisplay.defineControlKey(ControlKeys.MOVE_UP_KEY, 38);
    mapDisplay.defineControlKey(ControlKeys.MOVE_DOWN_KEY, 40);
    mapDisplay.defineControlKey(ControlKeys.MOVE_LEFT_KEY, 37);
    mapDisplay.defineControlKey(ControlKeys.MOVE_RIGHT_KEY, 39);
    mapDisplay.defineControlKey(ControlKeys.SELECT_KEY, 10);
    //for motorola
    mapDisplay.defineControlKey(ControlKeys.MOVE_UP_KEY, KEY_NUM2);
    mapDisplay.defineControlKey(ControlKeys.MOVE_DOWN_KEY, KEY_NUM8);
    mapDisplay.defineControlKey(ControlKeys.MOVE_LEFT_KEY, KEY_NUM4);
    mapDisplay.defineControlKey(ControlKeys.MOVE_RIGHT_KEY, KEY_NUM6);
    mapDisplay.defineControlKey(ControlKeys.SELECT_KEY, KEY_NUM5);

    mapDisplay.showZoomLevelIndicator(true);

    if (hasPointerEvents()) {
      mapDisplay.showDefaultOnScreenZoomControls();
    } else {
      mapDisplay.showDefaultCursor();
    }
    addCommand(exit);
    addCommand(GoToForm.GO_TO_COMMAND);
    addCommand(ResizeForm.RESIZE_COMMAND);
    addCommand(SetLocationForm.SET_MAP_COMPONENT_LOCATION);
    addCommand(RemovePlacesList.REMOVE_PLACES);
    // #if !release.build
    addCommand(ChangeMapList.CHANGE_MAP_COMMAND);
    // #endif
    addCommand(LogForm.SHOW_LOG);
    addCommand(BoundingBoxForm.SET_BOUNDING_BOX);
    addCommand(ChangeCursorList.CHANGE_CURSOR_COMMAND);
    addCommand(SearchForm.SEARCH_PLACENAMES);
    addCommand(PanningList.PANNING_STRATEGY);
    addCommand(ChangeZoomIndicator.CHANGE_ZOOM_INDICATOR);
    addCommand(SetZoomLevel.SET_ZOOM_LEVEL);
    addCommand(NetworkInformation.NETWORK_INFO);

    final ZoomRange zoomRange = mapDisplay.getZoomRange();
    System.out.println("Available zoom range for displayed map: minZoom = "
        + zoomRange.getMinZoom() + " maxZoom = " + zoomRange.getMaxZoom());

    setCommandListener(this);

    places = new Place[PLACE_NAMES.length];
    for (int i = 0; i < places.length; i++) {
      places[i] = new Place(i, new PlaceLabel(PLACE_NAMES[i], i % 4),
          icons[R.nextInt()], PLACE_COORDINATES[i][0], PLACE_COORDINATES[i][1]);
    }

    mapDisplay.addPlaces(places);

    mapDisplay.addKmlService(new KmlUrlReader(
      "http://lbs.nutiteq.ee/kml/ranna_tallinn.kml", false));
    
    initialized = true;
    repaint();
   }

  protected void paint(final Graphics g) {
    try {
      if (!initialized) {
        //paint splash
        g.setColor(0xFFFFFFFF);
        g.setClip(0, 0, getWidth(), getHeight());
        g.fillRect(0, 0, getWidth(), getHeight());
        g.drawImage(splash, (getWidth() - splash.getWidth()) / 2,
            (getHeight() - splash.getHeight()) / 2, Graphics.TOP | Graphics.LEFT);
        return;
      }

      g.setClip(0, 0, getWidth(), getHeight());
      g.setColor(0xFF444444);
      g.fillRect(0, 0, getWidth(), getHeight());

      mapDisplay.paint(g);
    } catch (final NullPointerException e) {
      Log.printStackTrace(e);
      final Alert a = new Alert("NPE", "NullPointer in paint", null, AlertType.ERROR);
      show(a);
    }
  }

  protected void keyPressed(final int keyCode) {
    if (!initialized) {
      return;
    }

    mapDisplay.keyPressed(keyCode);
  }

  protected void keyReleased(final int keyCode) {
    if (!initialized) {
      return;
    }
    mapDisplay.keyReleased(keyCode);
  }

  protected void keyRepeated(final int keyCode) {
    if (!initialized) {
      return;
    }
    mapDisplay.keyRepeated(keyCode);
  }

  protected void pointerDragged(final int x, final int y) {
    if (!initialized) {
      return;
    }
    mapDisplay.pointerDragged(x, y);
  }

  protected void pointerPressed(final int x, final int y) {
    if (!initialized) {
      return;
    }
    mapDisplay.pointerPressed(x, y);
  }

  protected void pointerReleased(final int x, final int y) {
    if (!initialized) {
      return;
    }
    mapDisplay.pointerReleased(x, y);
  }

  public void commandAction(final Command cmd, final Displayable d) {
    
    if (cmd == exit) {
      mapDisplay.stopMapping();
      try {
        Mapper.instance.destroyApp(true);
      } catch (final MIDletStateChangeException ignore) {
      }
    } else if (cmd == GoToForm.GO_TO_COMMAND) {
      Display.getDisplay(Mapper.instance).setCurrent(
          new GoToForm(Mapper.instance, instance, mapDisplay));
    } else if (cmd == ResizeForm.RESIZE_COMMAND) {
      Display.getDisplay(Mapper.instance).setCurrent(
          new ResizeForm(50, 50, getWidth(), getHeight(), mapDisplay));
    } else if (cmd == SetLocationForm.SET_MAP_COMPONENT_LOCATION) {
      Display.getDisplay(Mapper.instance).setCurrent(
          new SetLocationForm(0, 0, getWidth() - mapDisplay.getWidth(), getHeight()
              - mapDisplay.getHeight(), this));
    } else if (cmd == RemovePlacesList.REMOVE_PLACES) {
      Display.getDisplay(Mapper.instance).setCurrent(new RemovePlacesList(places, mapDisplay));
    } else if (cmd == LogForm.SHOW_LOG) {
      Display.getDisplay(Mapper.instance)
          .setCurrent(new LogForm(mapDisplay, Mapper.instance, this));
    } else if (cmd == BoundingBoxForm.SET_BOUNDING_BOX) {
      Display.getDisplay(Mapper.instance).setCurrent(new BoundingBoxForm(mapDisplay));
    } else if (cmd == ChangeCursorList.CHANGE_CURSOR_COMMAND) {
      Display.getDisplay(Mapper.instance).setCurrent(new ChangeCursorList(mapDisplay));
    } else if (cmd == SearchForm.SEARCH_PLACENAMES) {
      Display.getDisplay(Mapper.instance)
          .setCurrent(
              new SearchForm(this, mapDisplay
                  .getMiddlePoint()));
    } else if (cmd == searchResultsCmd) {
      Display.getDisplay(Mapper.instance).setCurrent(resultsList(kmlPlaces));
    } else if (cmd == resultsBack) {
      Display.getDisplay(Mapper.instance).setCurrent(this);
    } else if (cmd == resultsClear) {
      mapDisplay.removePlaces(searchPlaces);
      kmlPlaces = new KmlPlace[0];
      Display.getDisplay(Mapper.instance).setCurrent(this);
    } else if (cmd == resultsCenter && kmlPlaces.length > 0) {
      System.out.println(kmlPlaces.length + " : " + resultsList.getSelectedIndex());
      mapDisplay.setMiddlePoint(kmlPlaces[resultsList.getSelectedIndex()].getPlace().getWgs());
      Display.getDisplay(Mapper.instance).setCurrent(this);
    } else if (cmd == PanningList.PANNING_STRATEGY) {
      Display.getDisplay(Mapper.instance).setCurrent(new PanningList(mapDisplay));
    } else if (cmd == ChangeZoomIndicator.CHANGE_ZOOM_INDICATOR) {
      Display.getDisplay(Mapper.instance).setCurrent(new ChangeZoomIndicator(mapDisplay));
    } else if (cmd == SetZoomLevel.SET_ZOOM_LEVEL) {
      show(new SetZoomLevel(this, mapDisplay));
    } else if (cmd == NetworkInformation.NETWORK_INFO) {
      show(new NetworkInformation(this, mapDisplay.getDownloadCounter()));
    }
    // #if !release.build
    else if (cmd == ChangeMapList.CHANGE_MAP_COMMAND) {
      Display.getDisplay(Mapper.instance).setCurrent(
          new ChangeMapList(Mapper.instance, instance, mapDisplay));
    }
    // #endif
  }

  public void show(final Displayable next) {
    Display.getDisplay(Mapper.instance).setCurrent(next);
  }

  public void mapClicked(final WgsPoint p) {
    System.out.println("Map clicked " + p.getLon() + " : " + p.getLat());
  }

  public void mapMoved() {

  }

  public void needRepaint(final boolean mapIsComplete) {
    repaint();
  }

  public void elementClicked(final OnMapElement p) {

  }

  public void setPaintLocation(final int screenX, final int screenY) {
    mapDisplay.setScreenPosition(screenX, screenY);
  }

  public void networkError(final String message) {
    System.out.println("Network error: " + message);
  }

  public void licenseError(final String message) {
    System.out.println("License error: " + message);
  }

  public void elementEntered(final OnMapElement p) {

  }

  public void elementLeft(final OnMapElement p) {

  }

  public void searchResults(final KmlPlace[] kPlaces) {
    Display.getDisplay(Mapper.instance).setCurrent(resultsList(kPlaces));
    if (kPlaces.length == 0) {
      return;
    }
    this.kmlPlaces = kPlaces;
    searchPlaces = new Place[kPlaces.length];
    for (int i = 0; i < kPlaces.length; i++) {
      searchPlaces[i] = kPlaces[i].getPlace();
    }
    mapDisplay.addPlaces(searchPlaces);
    addCommand(searchResultsCmd);
  }

  private Displayable resultsList(final KmlPlace[] kPlaces) {
    resultsList = new List("Search results", List.IMPLICIT);
    if (kPlaces.length == 0) {
      resultsList.append("No results", null);
    } else {
      for (int i = 0; i < kPlaces.length; i++) {
        resultsList.append(kPlaces[i].getPlace().getName()+" "+kPlaces[i].getDescription(), null);
      }
    }

    resultsBack = new Command("Back", Command.BACK, 0);
    resultsCenter = new Command("Center", Command.OK, 0);
    resultsClear = new Command("Clear", Command.ITEM, 0);

    resultsList.addCommand(resultsBack);
    resultsList.addCommand(resultsCenter);
    resultsList.addCommand(resultsClear);
    resultsList.setCommandListener(this);

    return resultsList;
  }

}
